﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Schema;

namespace IniFileManager
{
	/// <summary>
	/// Class representing the structure of ini schema defined 
	/// by library's user as xml stream valid with embedded "IniSchema.xsd".
	/// </summary>
	public sealed class IniSchema
	{
		private const string invalidSchemaErrorMessage = "Provided ini schema stream had invalid format: {0}";
		private readonly char elementDelimiter;
		private readonly Dictionary<string, IniSectionSchema> sections;

		/// <summary>
		/// Creates "IniSchema" from ini schema xml representation.
		/// </summary>
		/// <param name="iniSchemaStream">Ini schema xml stream.</param>
		/// <exception cref="System.FormatException">Trown if is provided ini schema stream not valid.</exception>
		public IniSchema( Stream iniSchemaStream )
		{
			XmlReader iniSchemaXmlReader = XmlReader.Create( iniSchemaStream, IniSchemaXsd.validationSettings );
			XmlDocument iniSchemaXml = new XmlDocument();
			try
			{
				// Loading also performs validation of "iniSchemaXml" with embedded "IniSchema.xsd".
				// In case of invalid "iniSchemaStream" with "IniSchema.xsd" loading throws "XmlSchemaValidationException":
				iniSchemaXml.Load( iniSchemaXmlReader );
			}
			catch( XmlSchemaValidationException e )
			{
				throw new FormatException( String.Format( invalidSchemaErrorMessage, e.Message ) );
			}

			// Embedded "IniSchema.xsd"'s definition guarantees that root element is "iniSchema":
			XmlNode iniSchemaNode = iniSchemaXml.DocumentElement;
			// Embedded "IniSchema.xsd"'s definition guarantees that value of this attribute is char:
			this.elementDelimiter = char.Parse( iniSchemaNode.Attributes[IniSchemaXsd.ATTRIBUTE_ELEMENT_DELIMITER].Value );
			this.sections = new Dictionary<string, IniSectionSchema>();

			// Embedded "IniSchema.xsd"'s definition guarantees that every child of "iniSchema" is "section":
			XmlNodeList sectionNodes = iniSchemaNode.ChildNodes;
			foreach( XmlNode sectionNode in sectionNodes )
			{
				try
				{
					// In case of semantical exceptions in "iniSchemaStream" we can expect 
					// "FormatException" while trying to construct "IniScetionSchema" from "sectionNode".
					IniSectionSchema section = new IniSectionSchema( sectionNode );
					sections.Add( section.Name, section );
				}
				catch( FormatException e )
				{
					throw new FormatException( String.Format( invalidSchemaErrorMessage, e.Message ) );
				}
			}
		}

		/// <summary>
		/// Character interpreted as element delimiter within.
		/// </summary>
		internal char ElementDelimiter
		{
			get
			{
				return this.elementDelimiter;
			}
		}
		/// <summary>
		/// Hash map of section schemas within.
		/// </summary>
		internal Dictionary<string, IniSectionSchema> Sections
		{
			get
			{
				return this.sections;
			}
		}

	}
}
